﻿/*
 * This file is part of CommunitySettlers.
 *
 * CommunitySettlers is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * CommunitySettlers is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with CommunitySettlers.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author: Christoph Husse
 * 
 * Also checkout our homepage: http://communitysettlers.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace SettlersEngine
{


    internal class ProtectedEnumerable<T> : IEnumerable<T>
    {
        private IEnumerable<T> m_Protected;

        public ProtectedEnumerable(IEnumerable<T> inEnumerable)
        {
            m_Protected = inEnumerable;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return m_Protected.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return m_Protected.GetEnumerator();
        }
    }

    internal partial class RuntimeGrid
    {
        private const Int32 RESOURCE_TOPOLOGY_GRANULARITY = 10;
        private static readonly TimeSpan COLLISION_TIMESPAN = TimeSpan.FromSeconds(1);

        private RuntimeGridCell[,] m_Cells;
        private readonly LinkedList<IMovableObject> m_Movables = new LinkedList<IMovableObject>();
        private readonly LinkedList<IBuildableObject> m_Buildings = new LinkedList<IBuildableObject>();
        private readonly LinkedList<IResourceObject> m_Resources = new LinkedList<IResourceObject>();
        private readonly TopologicalList<IResourceObject> m_ResourceProvs;
        private readonly TopologicalList<IMovableObject> m_Settlers;
        private readonly LinkedList<IResourceObject> m_ResProvQueue;
        private readonly LinkedList<ResourceAnimation> m_ResourceAnimations;
        private readonly LinkedList<IResourceObject>[] m_ResQueryPrioQueue;
        private readonly MovableManager<RuntimeGridCell> m_MovableManager;
        //private readonly NativeEngine.AStarSolver<RuntimeGridCell> m_AStar;
        private readonly Random m_Random = new Random();

        public ProtectedEnumerable<IResourceObject>[] ResourceQueryQueue { get; private set; }
        public Rectangle Bounds { get { return new Rectangle(0, 0, Width - 1, Height - 1); } }
        public ProtectedEnumerable<IMovableObject> Movables { get; private set; }
        public ProtectedEnumerable<IBuildableObject> Buildings { get; private set; }
        public ProtectedEnumerable<IResourceObject> Resources { get; private set; }
        public int Width { get; private set; }
        public int Height { get; private set; }
        public RuntimeMap Map { get; private set; }
#if DEBUG
        [ThreadStatic]
        private static Boolean m_IsGridContext;
        public static Boolean IsGridContext { get { return m_IsGridContext; } private set { m_IsGridContext = value; } }
#endif

        private RuntimeGridCell this[Point position]
        {
            get { return m_Cells[position.X, position.Y]; }
        }

        private RuntimeGridCell this[Int32 inX, Int32 inY]
        {
            get { return m_Cells[inX, inY]; }
        }

        public RuntimeGrid(RuntimeMap inParent, int inWidth, int inHeight)
        {
            if (inParent == null)
                throw new ArgumentNullException();

            m_Settlers = new TopologicalList<IMovableObject>(RESOURCE_TOPOLOGY_GRANULARITY, Width, Height);
            m_ResourceProvs = new TopologicalList<IResourceObject>(RESOURCE_TOPOLOGY_GRANULARITY, Width, Height);
            m_ResProvQueue = new LinkedList<IResourceObject>();
            m_ResQueryPrioQueue = new LinkedList<IResourceObject>[RuntimeMap.MAX_RESOURCE_STACK];
            ResourceQueryQueue = new ProtectedEnumerable<IResourceObject>[m_ResQueryPrioQueue.Length];

            for (int i = 0; i < m_ResQueryPrioQueue.Length; i++)
            {
                m_ResQueryPrioQueue[i] = new LinkedList<IResourceObject>();
                ResourceQueryQueue[i] = new ProtectedEnumerable<IResourceObject>(m_ResQueryPrioQueue[i]);
            }

            Map = inParent;
            Width = inWidth;
            Height = inHeight;
            Movables = new ProtectedEnumerable<IMovableObject>(m_Movables);
            Buildings = new ProtectedEnumerable<IBuildableObject>(m_Buildings);
            Resources = new ProtectedEnumerable<IResourceObject>(m_Resources);

            m_Cells = new RuntimeGridCell[Width, Height];

            for (int x = 0; x < Width; x++)
            {
                for (int y = 0; y < Height; y++)
                {
                    m_Cells[x, y] = new RuntimeGridCell(this, x, y);
                }
            }

            //m_AStar = new NativeEngine.AStarSolver<RuntimeGridCell>(m_Cells);
            m_MovableManager = new MovableManager<RuntimeGridCell>(m_Cells, Map.AnimationTime.CycleCount, (int)RuntimeMap.GRIDPOS_TO_CYCLES);
        }

        public IPositionProvider GetObjectAtPosition(System.Windows.Point inPos)
        {
            // TODO: use PixelShader for color coded selection
            return m_Cells[(int)inPos.X, (int)inPos.Y].FixedObject;
        }

        public bool CanPlaceObject(IPositionProvider inObject)
        {
            return CanPlaceObject(inObject, inObject.GridPosition);
        }

        public bool CanPlaceObject(IPositionProvider inObject, System.Drawing.Point inPosition)
        {
            if (inObject is IMovableObject)
            {
                IMovableObject movable = inObject as IMovableObject;

                for (int x = 0; x < movable.Size; x++)
                {
                    for (int y = 0; y < movable.Size; y++)
                    {
                        int gridX = x + inPosition.X;
                        int gridY = y + inPosition.Y;

                        if ((gridX < 0) || (gridX >= Width - 1) || (gridY < 0) || (gridY >= Height - 1))
                            return false;

                        RuntimeGridCell item = m_Cells[gridX, gridY];

                        if (item.FixedObject != null)
                            return false;
                    }
                }
            }
            else if (inObject is IBuildableObject)
            {
                IBuildableObject buildable = inObject as IBuildableObject;

                for (int x = buildable.LockedBounds.Left; x < buildable.LockedBounds.Right; x++)
                {
                    for (int y = buildable.LockedBounds.Top; y < buildable.LockedBounds.Bottom; y++)
                    {
                        int gridX = x + inPosition.X;
                        int gridY = y + inPosition.Y;

                        if ((gridX < 0) || (gridX >= Width - 1) || (gridY < 0) || (gridY >= Height - 1))
                            return false;

                        RuntimeGridCell item = m_Cells[gridX, gridY];

                        if ((item.FixedObject != null) && !(item.FixedObject is IMovableObject))
                            return false;
                    }
                }
            }
            else if (inObject is IResourceObject)
            {
                IResourceObject resource = inObject as IResourceObject;

                for (int x = resource.Position.XGrid - 1, right = resource.Position.XGrid + 1, bottom = resource.Position.YGrid + 1,
                    top = resource.Position.YGrid - 1; x < right; x++)
                {
                    for (int y = top; y < bottom; y++)
                    {
                        if ((x < 0) || (x >= Width - 1) || (y < 0) || (y >= Height - 1))
                            return false;

                        RuntimeGridCell item = m_Cells[x, y];

                        if (item.Resource != null)
                            return false;
                    }
                }

                IPositionProvider fixedObject = m_Cells[resource.Position.XGrid, resource.Position.YGrid].FixedObject;

                if (fixedObject != null)
                {
                    if (!(fixedObject is IMovableObject))
                        return false;
                }
            }
            else
                throw new ArgumentException("Unknown object type.");

            return true;
        }
    }
}
